package com.blb.day20231226redis.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blb.day20231226redis.mapper.ScoreMapper;
import com.blb.day20231226redis.pojo.Score;
import com.blb.day20231226redis.service.ScoreService;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class ScoreServiceImpl extends ServiceImpl<ScoreMapper, Score> implements ScoreService {

    public static final String PREFIX = "Score:";

    @Autowired
    private ScoreMapper scoreMapper;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private RBloomFilter<String> bloomFilter;

    @Override
    public Score getScoreById(Long id) {
        //获得string类型操作对象
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        Score value = (Score) ops.get(PREFIX + id);
        //使用DCL双检锁提升查询性能
        if(value == null) {
            synchronized (this) {
                //先查询redis，redis没有再查mysql
                value = (Score) ops.get(PREFIX + id);
                if (value == null) {
                    System.out.println("Redis为空，访问数据库! " + Thread.currentThread().getName());
                    //使用布隆过滤器判断数据库没有的id
                    if(!bloomFilter.contains(PREFIX + id)){
                        System.out.println("数据库为空，返回空值 " + Thread.currentThread().getName());
                        return null;
                    }
                    Score score = scoreMapper.selectById(id);
                    //查询数据库不为空，就保存到redis中
                    if (score != null) {
                        System.out.println("数据库不为空，就保存到redis中 " + Thread.currentThread().getName());
                        ops.set(PREFIX + id, score);
                        return score;
                    }
//                    else {
//                        System.out.println("数据库为空，返回空值 " + Thread.currentThread().getName());
//                        //解决穿透问题，向Redis保存空值，设置过期
//                        ops.set(PREFIX + id, new Score(), 10, TimeUnit.SECONDS);
//                    }
                    return null;
                }
            }
        }
        System.out.println("redis存在就直接返回数据" + Thread.currentThread().getName());
        //如果redis存在就直接返回
        return value;
    }

    @Cacheable(cacheNames = "Scores")
    @Override
    public List<Score> getAll() {
        return scoreMapper.selectAll();
    }

    @Cacheable(cacheNames = "ScorePage",key = "T(String).valueOf(#current)")
    @Override
    public IPage<Score> getScorePage(Long current, Long size) {
        return scoreMapper.selectScorePage(new Page<>(current,size));
    }

    //加到更新或添加方法上，根据key判断，是添加或更新缓存
//    @CachePut(cacheNames = "Score",key = "T(String).valueOf(#score.id)")
    //组合式注解，更新Score缓存时，同时删除Scores和ScorePage两个缓存
    @Caching(
            put = @CachePut(cacheNames = "Score",key = "T(String).valueOf(#score.id)"),
            evict = {
                    @CacheEvict(cacheNames = "Scores",allEntries = true),
                    @CacheEvict(cacheNames = "ScorePage",allEntries = true),
            }
    )
    @Override
    public Score updateScore(Score score) {
        scoreMapper.updateById(score);
        return score;
    }

    @CachePut(cacheNames = "Score",key = "T(String).valueOf(#score.id)")
    @Override
    public Score saveScore(Score score) {
        scoreMapper.insert(score);
        return score;
    }

    //加删除方法，同时删除缓存
    @CacheEvict(cacheNames = "Score",key = "T(String).valueOf(#id)")
    @Override
    public void deleteScore(Long id) {
        scoreMapper.deleteById(id);
    }
}
